export function isPrimative(val: any) {
  const isObject = ['Object', 'Array'].includes(
    Object.prototype.toString.call(val).slice(8, -1)
  );
  return !isObject;
}

export function camelCase(str: string) {
  return str.replace(/[-_](\w)/g, (_, c) => c.toUpperCase());
}

export const isPlainObject = (val: any): boolean => {
  return Object.prototype.toString.call(val).slice(8, -1) === 'Object';
};

export const isSameData = (data1: any, data2: any) => {
  if (data1 === data2) {
    return true;
  }

  if (
    (isPlainObject(data1) && isPlainObject(data2)) ||
    (Array.isArray(data1) && Array.isArray(data2))
  ) {
    return JSON.stringify(data1) === JSON.stringify(data2);
  }

  return data1 == data2;
};

export const pickData = (data: Obj, keys: Array<string>, defVal = '') => {
  return keys.reduce((result, key) => {
    result[key] = data[key] === undefined ? defVal : data[key];
    return result;
  }, {} as Obj);
};

export const sleep = (seconds = 1) => {
  return new Promise(resolve => setTimeout(resolve, seconds * 1000));
};

export function setDefaults<T extends Obj>(data: T, defaultData: Obj): T {
  Object.keys(defaultData).forEach(key => {
    const value = data[key];
    if (value == undefined) {
      // @ts-ignore
      data[key] = defaultData[key];
    }
  });

  return data;
}

export function uuid(len = 8) {
  return Array.from({ length: len + 1 })
    .join('x')
    .replace(/[x]/g, () => {
      const r = (Math.random() * 16) | 0;
      return r.toString(16);
    });
}

export function getRandomInt(max: number, min = 0) {
  return min + Math.floor(Math.random() * (max - min + 1));
}

export function getRandPassword(length = 6) {
  const tempPwd = Array.from({ length })
    .map(() => getRandomInt(9))
    .join('');

  return tempPwd;
}

export function capitalize(str: string) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

export function parsable(val: any) {
  const type = typeof val;
  if (['null', 'number', 'boolean'].includes(type)) {
    return true;
  }

  if (type === 'string') {
    const jsonRe = /^\[.*\]$|^\{.*\}$/;
    const numRe = /^\d+(\.\d+)?$/;
    return jsonRe.test(val) || numRe.test(val);
  }

  return false;
}

export function tryParse(val: any) {
  if (parsable(val)) {
    return JSON.parse(val);
  }

  return val;
}

export function formatCurrency(
  value: string | number,
  decimal: number = 2,
  prefix: string = '$',
  thousandSeparator: string = ','
) {
  const pre = Number(value) < 0 ? '-' : '';
  const valNum = Math.abs(Number(value));
  if (isNaN(valNum)) return value;
  const valStr = decimal > 0 ? valNum.toFixed(decimal) : String(Number(valNum));
  const re = /(?<=^\d+)\B(?=(\d{3})+(\.|$))/g;
  return pre + prefix + valStr.replace(re, thousandSeparator);
}

export function debounce(func: Fn, delaySec = 1, ctx?: any) {
  let timeout: NodeJS.Timeout | null = null;
  return function (...args: any[]) {
    if (timeout) {
      clearTimeout(timeout);
    }

    // @ts-ignore
    const that = ctx || this;
    timeout = setTimeout(function () {
      func.apply(that, args);
    }, delaySec * 1000);
  };
}

export function throttle(func: Fn, delaySec = 1, ctx?: any) {
  let lastTime = 0;
  return function (...args: any[]) {
    const curTime = Date.now();
    if (curTime - lastTime < delaySec * 1000) {
      return;
    }

    // @ts-ignore
    const that = ctx || this;
    func.apply(that, args);
    lastTime = curTime;
  };
}
// same as Object.keys(), but has type infer correctly
export function getKeys<T = Obj>(obj: T): (keyof T)[] {
  const keys: (keyof T)[] = [];
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      keys.push(key);
    }
  }

  return keys;
}
